import importlib
import os
import platform
import sys
import threading
import time

try:
    import guizero
    from PyInstaller.__main__ import run
except:
    os.system(
        "python -m pip install guizero pyinstaller -i https://mirrors.aliyun.com/pypi/simple --user"
    )
    import guizero
    from PyInstaller.__main__ import run

SYS = platform.system()
path = os.path.dirname(__file__)
os.chdir(path)  # 使用程序所在目录作为工程目录，避免双击打开之后使用system文件夹作为目录，没有写文件权限
# print(path)


class myApp(guizero.App):
    "将py转换成app的库"

    def __init__(self):
        "初始化"
        guizero.App.__init__(self, "py2app", width=600, height=550, bg="white")
        w, h = self.width, self.height
        self.tk.maxsize(w * 2, h)
        self.draw()
        self.display()

    def draw(self):
        "绘制主界面"
        guizero.Text(self, "本软件用于打包Python编写的程序\n仅供学习\n谢谢使用", size=14, font="楷体")
        guizero.Box(self, width="fill", height=1, border=1)
        infoBox = guizero.Box(self, layout="grid")
        guizero.Text(infoBox, "图标文件：", size=12, font="楷体", grid=[0, 0], align="left")
        guizero.Text(infoBox, "源码文件：", size=12, font="楷体", grid=[0, 1], align="left")
        guizero.Text(infoBox, "输出位置：", size=12, font="楷体", grid=[0, 2], align="left")
        guizero.Text(infoBox, "程序名称：", size=12, font="楷体", grid=[0, 3], align="left")
        self.iconPathText = guizero.Text(
            infoBox, size=12, font="楷体", grid=[1, 0], color="#ff88ff", align="left"
        )
        self.pyPathText = guizero.Text(
            infoBox, size=12, font="楷体", grid=[1, 1], color="#003311", align="left"
        )
        self.out = guizero.Text(
            infoBox,
            f"{os.path.dirname(os.path.abspath(__file__))}",
            size=12,
            font="楷体",
            grid=[1, 2],
            color="#443366",
            align="left",
        )
        self.app_name = guizero.TextBox(
            infoBox, text="未命名程序.exe", grid=[1, 3], width=30, align="left"
        )
        self.outPath = os.path.dirname(os.path.abspath(__file__))
        self.imageBox = guizero.Picture(self)

        setBox = guizero.Box(self, layout="grid")
        guizero.PushButton(
            setBox, command=self.selectIcon, text="选择图标(可选)", grid=[0, 0]
        )
        guizero.PushButton(
            setBox, command=self.selectOut, text="选择输出目录(可选)", grid=[1, 0]
        )
        guizero.PushButton(setBox, command=self.selectPy, text="选择程序(必选)", grid=[2, 0])

        cfgBox = guizero.Box(setBox, grid=[0, 1, 3, 1], layout="grid")
        guizero.Text(cfgBox, "打包单文件", grid=[0, 0], font="楷体")
        guizero.Text(cfgBox, "临时目录支持", grid=[0, 1], font="楷体")
        guizero.Text(cfgBox, "隐藏命令行", grid=[0, 2], font="楷体")
        guizero.Text(cfgBox, "特殊库检测", grid=[0, 3], font="楷体")
        guizero.Box(cfgBox, grid=[0, 4, 2, 1], border=1, width="fill", height=1)
        guizero.Text(
            cfgBox,
            "如果打包后遇到模块缺失问题\n请在下方手动添加模块(空格分隔)",
            grid=[0, 5, 2, 1],
            font="楷体",
            color="#ff2233",
        )
        self.ifOnefile = guizero.ButtonGroup(
            cfgBox,
            options=["是", "否"],
            selected="是",
            horizontal=True,
            grid=[1, 0],
            command=self.appPathOption,
        )
        self.ifAddPath = guizero.ButtonGroup(
            cfgBox, options=["是", "否"], selected="是", horizontal=True, grid=[1, 1]
        )
        self.ifWindow = guizero.ButtonGroup(
            cfgBox, options=["是", "否"], selected="是", horizontal=True, grid=[1, 2]
        )
        self.ifcheck = guizero.ButtonGroup(
            cfgBox, options=["是", "否"], selected="是", horizontal=True, grid=[1, 3]
        )
        self.extraLib = guizero.TextBox(
            cfgBox,
            grid=[0, 6, 2, 1],
            width=28,
            height=3,
            multiline=True,
            scrollbar=True,
        )

        runBox = guizero.Box(cfgBox, grid=[0, 7, 2, 1], layout="grid")
        self.createButton = guizero.PushButton(
            runBox,
            command=self.createInfo,
            text="生成打包脚本",
            grid=[0, 2],
            width=14,
            enabled=False,
        )
        self.packageButton = guizero.PushButton(
            runBox,
            command=self.package,
            text="打包",
            grid=[1, 2],
            width=13,
            enabled=False,
        )
        self.infoText = guizero.Text(
            self,
            text="",
            font="宋体",
        )

    def appPathOption(self):
        "临时文件默认选项"
        if self.ifOnefile.value == "是":
            self.ifAddPath.value = "是"
        else:
            self.ifAddPath.value = "否"

    def selectIcon(self):
        "选择图标"
        iconPath = guizero.select_file("选择图标", filetypes=[["图片", ".ico"]])
        if SYS == "Windows":
            iconPath = iconPath.replace("/", "\\")
        if os.path.isfile(iconPath):
            self.iconPathText.value = iconPath
            self.imageBox.image = iconPath
            self.imageBox.width = self.imageBox.height = 64
        else:
            self.iconPathText.value = ""

    def selectPy(self):
        "选择程序入口文件"
        pyPath = guizero.select_file("选择程序入口文件", filetypes=[["Python程序文件", "*.py*"]])
        if os.path.isfile(pyPath):
            if SYS == "Windows":
                pyPath = pyPath.replace("/", "\\")
            self.pyPathText.value = pyPath
            self.createButton.enable()
            self.packageButton.enable()
            name = os.path.split(pyPath)[1]
            name = name.split(".")[:-1]
            name = "".join(name) + ".exe"
            self.app_name.value = name
        else:
            self.pyPathText = ""
            self.createButton.disable()
            self.packageButton.disable()

    def selectOut(self):
        "选择输出目录"
        outPath = guizero.select_folder("选择输出目录")
        if os.path.isdir(outPath):
            self.out.value = outPath
        self.outPath = outPath

    def package(self):
        "打包"
        self.options = self.createOptions()
        if self.options:
            self.createButton.disable()
            self.packageButton.disable()
            self.end = 0
            self.t1 = threading.Thread(target=self.newThread)
            self.t1.start()
            self.t2 = threading.Thread(target=self.showInfo)
            self.t2.start()
        else:
            self.packageButton.enable()

    def showInfo(self):
        "显示信息"
        self.infoText.value = "打包中"
        startTime = time.time()
        while self.end == 0:
            n = int(time.time() * 10) % 30 + 1
            self.infoText.value = "打包中" + "." * n
        guizero.info("提示", f"打包成功，用时{time.time()-startTime:.2f}s，谢谢使用！")
        self.infoText.value = "打包完成！"
        self.packageButton.enable()
        self.t1.join()

    def newThread(self):
        "新线程"
        run(self.options)
        self.end = 1

    def createOptions(self):
        "创建options"
        pyPath = self.pyPathText.value
        path = os.path.dirname(pyPath)
        options = [
            f"{self.pyPathText.value}",
            # 设定输出目录为outPath
            f"--distpath={self.outPath}",
        ]
        self.myFiles = []
        if self.ifAddPath.value == "是":
            print("添加临时目录支持...")
            self.addPath(pyPath)  # 添加临时目录支持
        if self.ifcheck.value == "是":
            print("特殊库检测...")
            self.specialCheck()
        self.myFiles += self.getFiles(path)
        for nowFile in self.myFiles:
            if SYS == "Windows":
                nowFile = [i.replace("/", "\\") for i in nowFile]
            if pyPath != nowFile[0]:
                options.append(f"--add-data={nowFile[0]};{nowFile[1]}")
        if self.iconPathText.value != "":
            options.append(f"--icon={self.iconPathText.value}")

        if self.ifOnefile.value == "是":
            options.append("--onefile")
        if self.ifWindow.value == "是":
            options.append("--windowed")
        options.append("--noupx")
        strs = self.extraLib.value.replace("\n", "")
        options.append("--clean")
        options.append(f"--name={self.app_name.value}")
        if strs:
            libs = strs.split(" ")
            print(libs)
            for nowLib in libs:
                nowLib = nowLib
                module = importlib.util.find_spec(nowLib)
                if module:
                    options.append(f"--hidden-import={nowLib}")
                else:
                    self.error("错误", f"{nowLib}模块未安装，请安装后再尝试！")
                    return 0
        print(options)
        strings = (
            f"from PyInstaller.__main__ import run\noptions={options}\nrun(options)"
        )
        path = os.path.join(self.outPath, "run.py")
        with open(path, "w+", encoding="utf-8") as f:
            f.write(strings)
        self.runPath = path
        return options

    def createInfo(self):
        "生成打包脚本"
        self.createOptions()
        guizero.info("提示", f"打包脚本生成，请运行{self.runPath}文件尝试进行打包。")

    def getFiles(self, path):
        "获取文件"
        myFiles = []
        datas = os.listdir(path)
        for data in datas:
            ifAdd = 1
            if data.startswith(".git") or data.startswith("__"):
                ifAdd = 0
            if "cache" in data:
                ifAdd = 0
            if ifAdd:
                name = os.path.join(path, data)
                if os.path.isdir(name):
                    myFiles.append([name, data])
                else:
                    myFiles.append([name, "."])
        return myFiles

    def addLib(self, libName):
        "添加库"
        print(f"添加{libName}库支持...")
        pythonPath = sys.executable  # 获取python.exe位置
        libPath = os.path.dirname(os.path.dirname(pythonPath))
        libPath = os.path.join(libPath, "Lib", "site-packages")
        if not os.path.isdir(libPath):
            try:
                libPath = sys.path[5]
            except:
                guizero.error("错误", "python库目录获取失败，请检查python是否正常安装！")
        libPath = os.path.join(libPath, libName)

        def check(libPath):
            if os.path.isdir(libPath):
                self.myFiles.append([libPath, libName])
            else:
                ask = guizero.yesno("库文件获取错误", f"{libPath}库添加失败，是否手动输入地址？")
                if ask == True:
                    libPath = guizero.askstring(f"请输入{libPath}库地址：")
                    check(libPath)

        check(libPath)

    def addPath(self, pyPath):
        "单文件打包临时目录支持"
        lines = """
#####################################################################
#打包单文件支持
import os
import sys
PATH=os.getcwd()#存储文件时候记得使用PATH变量组合创建绝对路径，否则会把数据写在临时目录
#print(PATH)
path=sys._MEIPASS if getattr(sys,"frozen",0) else PATH
os.chdir(path)
#print(os.getcwd())
#####################################################################
        """
        pyPath = self.pyPathText.value
        with open(pyPath, "r", encoding="utf-8") as f:
            data = f.read()
        if (not "_MEIPASS" in data) or "py2app" in data:
            data = lines + data
            with open(pyPath, "w", encoding="utf-8") as f:
                f.write(data)

    def specialCheck(self):
        "特殊库支持"
        pyPath = self.pyPathText.value
        with open(pyPath, "r", encoding="utf-8") as f:
            data = f.read()
        if "pgzrun" in data or "pgzero" in data or "cpgzh" in data:
            self.addLib("pgzero")


if __name__ == "__main__":
    myApp()
